const shader = require('../shader/index');
const MeshLine = require('../util/meshLine');
const bezierLine = require('../util/bezierLine');
const parseColor = require('../util/csscolorparse');
const initArrayBuffer = require('../util/initArrayBuffer');
const initIndexBuffer = require('../util/initIndexBuffer');
const glMatrix = require('gl-matrix');
    
module.exports = class DynamicLine {
    constructor(option) {
        this.id = option.id;
        this.gl = option.gl;
        const gl = option.gl;
        this.globe = option.globe;
        this.radius = option.radius;

        // this.color = parseColor(option.color);
        // this.lineWidth = option.lineWidth;
        // this.opacity = option.opacity;
        this.sizeAttenuation = option.sizeAttenuation;
        this.path = option.path;
        this.segmentCount = option.segmentCount;
        this.segmentGroup = option.segmentGroup;
        this.speed = option.speed;

        this.buffers = [];
        this.count = 0;
        this.timeNow = 0;

        // init shader
        const program = gl.createProgram();
        this.program = program;
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, shader.dynamicLine.fragmentSource);
        gl.compileShader(fragmentShader);
        gl.attachShader(program, fragmentShader);
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, shader.dynamicLine.vertexSource);
        gl.compileShader(vertexShader);
        gl.attachShader(program, vertexShader);
        gl.linkProgram(program);

        const preBuffer = {
            seq: [],
            position: [],
            previous: [],
            next: [],
            side: [],
            width: [],
            colors: [],
            opacitys: [],
            lineWidths: [],
            index: []
        }

        const g = new MeshLine();

        let indexLength = 50 * 2 + 1 + 1;

        for (let i = 0; i < this.path.length; i++) {
            const path = bezierLine.apply(null, this.path[i].slice(0, 4).concat([this.radius]));
            g.setGeometry(path, this.path[i][4], parseColor(this.path[i][5]), this.path[i][6], this.path[i][7]);

            preBuffer.seq = preBuffer.seq.concat(g.grometry.seq);
            preBuffer.position = preBuffer.position.concat(g.grometry.position);
            preBuffer.previous = preBuffer.previous.concat(g.grometry.previous);
            preBuffer.next = preBuffer.next.concat(g.grometry.next);
            preBuffer.side = preBuffer.side.concat(g.grometry.side);
            preBuffer.width = preBuffer.width.concat(g.grometry.width);
            preBuffer.colors = preBuffer.colors.concat(g.grometry.colors);
            preBuffer.opacitys = preBuffer.opacitys.concat(g.grometry.opacitys);
            preBuffer.lineWidths = preBuffer.lineWidths.concat(g.grometry.lineWidths);
            for(let j = 0; j < g.grometry.index.length; j++) {
                preBuffer.index.push(g.grometry.index[j] + i * indexLength);
            }
        };

        this.buffer = {
            seq: initArrayBuffer(gl, new Float32Array(preBuffer.seq)),
            position: initArrayBuffer(gl, new Float32Array(preBuffer.position)),
            previous: initArrayBuffer(gl, new Float32Array(preBuffer.previous)),
            next: initArrayBuffer(gl, new Float32Array(preBuffer.next)),
            side: initArrayBuffer(gl, new Float32Array(preBuffer.side)),
            width: initArrayBuffer(gl, new Float32Array(preBuffer.width)),
            colors: initArrayBuffer(gl, new Float32Array(preBuffer.colors)),
            opacitys: initArrayBuffer(gl, new Float32Array(preBuffer.opacitys)),
            lineWidths: initArrayBuffer(gl, new Float32Array(preBuffer.lineWidths)),
            indexBuffer: initIndexBuffer(gl, new Uint16Array(preBuffer.index)),
            n: preBuffer.index.length
        };
        
        // get attribLocation
        this.attribLocation = {
            position: gl.getAttribLocation(program, 'position'),
            seq: gl.getAttribLocation(program, 'seq'),
            previous: gl.getAttribLocation(program, 'previous'),
            next: gl.getAttribLocation(program, 'next'),
            side: gl.getAttribLocation(program, 'side'),
            width: gl.getAttribLocation(program, 'width'),
            color: gl.getAttribLocation(program, 'color'),
            opacity: gl.getAttribLocation(program, 'opacity'),
            lineWidth: gl.getAttribLocation(program, 'lineWidth')
        }
        
        // get uniformLocation
        this.uniformLocation = {
            projectionMatrix: gl.getUniformLocation(program, 'projectionMatrix'),
            viewMatrix: gl.getUniformLocation(program, 'viewMatrix'),
            modelMatrix: gl.getUniformLocation(program, 'modelMatrix'),
            resolution: gl.getUniformLocation(program, 'resolution'),
            // lineWidth: gl.getUniformLocation(program, 'lineWidth'),
            // color: gl.getUniformLocation(program, 'color'),
            // opacity: gl.getUniformLocation(program, 'opacity'),
            sizeAttenuation: gl.getUniformLocation(program, 'sizeAttenuation'),
            startSeq: gl.getUniformLocation(program, 'startSeq'),
            endSeq: gl.getUniformLocation(program, 'endSeq'),
            segments: gl.getUniformLocation(program, 'segments'),
        }
    }

    render(config) {
        const model = config.modelMatrix;
        const view = config.viewMatrix;
        const projection = config.projMatrix;
        const normalMatrix = config.normalMatrix;

        if (this.timeNow) {
            const time = Date.now();
            this.count += this.speed * (time - this.timeNow);
            this.timeNow = time;
        }
        else {
            this.timeNow = Date.now();
        }

        if (this.count > this.segmentCount) this.count = 0;

        const gl = this.gl;
        const windowWidth = gl.drawingBufferWidth;
        const windowHeight = gl.drawingBufferHeight;
        gl.useProgram(this.program);
        
        // gl.disable(gl.DEPTH_TEST);
        // gl.disable(gl.CULL_FACE);
        gl.enable(gl.BLEND);
        // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE);

        gl.uniformMatrix4fv(this.uniformLocation.projectionMatrix, false, projection);
        gl.uniformMatrix4fv(this.uniformLocation.viewMatrix, false, view);
        gl.uniformMatrix4fv(this.uniformLocation.modelMatrix, false, model);
        gl.uniform2f(this.uniformLocation.resolution, windowWidth, windowHeight);
        // gl.uniform3fv(this.uniformLocation.color, this.color);
        // gl.uniform1f(this.uniformLocation.lineWidth, this.lineWidth);
        // gl.uniform1f(this.uniformLocation.opacity, this.opacity);
        gl.uniform1f(this.uniformLocation.sizeAttenuation, this.sizeAttenuation);
        gl.uniform1f(this.uniformLocation.startSeq, (this.count - this.segmentGroup));
        gl.uniform1f(this.uniformLocation.endSeq, this.count);
        gl.uniform1f(this.uniformLocation.segments, this.segmentGroup);

        // for (let i = 0; i < this.buffers.length; i++) {
        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.position);
        gl.vertexAttribPointer(this.attribLocation.position, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.position);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.previous);
        gl.vertexAttribPointer(this.attribLocation.previous, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.previous);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.next);
        gl.vertexAttribPointer(this.attribLocation.next, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.next);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.side);
        gl.vertexAttribPointer(this.attribLocation.side, 1, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.side);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.width);
        gl.vertexAttribPointer(this.attribLocation.width, 1, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.width);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.colors);
        gl.vertexAttribPointer(this.attribLocation.color, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.color);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.opacitys);
        gl.vertexAttribPointer(this.attribLocation.opacity, 1, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.opacity);
        
        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.lineWidths);
        gl.vertexAttribPointer(this.attribLocation.lineWidth, 1, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.lineWidth);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.seq);
        gl.vertexAttribPointer(this.attribLocation.seq, 1, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.seq);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffer.indexBuffer);

        gl.drawElements(gl.TRIANGLES, this.buffer.n, gl.UNSIGNED_SHORT, 0); 
        // } 
        gl.disable(gl.BLEND);
    }
}
